#include "aerial\Object\TextInput.h"

using namespace air;

TextInput::TextInput(hgeResourceManager* _hgeResMan, float _x, float _y, float _width, float _height, DWORD _color, int _capacity, const char *_text)
	: IGuiObject(_hgeResMan), capacity(_capacity), isMultiLine(false), enterHook(0),
	focused(false), maxSize(16), blinkCounter(0), tabInsert(false)
{
	rect.Set(_x, _y, _x + _width, _y + _height);
	fnt = hgeResMan->GetFont("fnt.original");

	// style
	color.SetHWColor(_color);
	borderColorNorm.SetHWColor(0xFFAAAAAA);		// default white
	borderColorOver.SetHWColor(0xFFFFFF00);
	borderColorFocused.SetHWColor(0xFF00FFFF);
	borderColor = borderColorNorm;
	padTop      = 1.0f;
	padLeft     = 3.0f;
	cursorXOffset = 0.07f;
	cursorYOffset = 0.0f;
	blinkInterval = 0.62f;

	ox = _x;
	oy = _y;
	text = new char[capacity + 1];
	SetText(_text);
	pos = size;					// size will be initialized in SetText()

	updateCursorPos();
}

TextInput::TextInput(hgeResourceManager* hgeResMan, float _width, float _height, DWORD _color, int _capacity, const char *_text)
	: IGuiObject(hgeResMan), capacity(_capacity), isMultiLine(false), enterHook(0),
	focused(false), maxSize(16), blinkCounter(0), tabInsert(false)
{
	rect.Set(0, 0, _width, _height);
	fnt = hgeResMan->GetFont("fnt.original");

	// style
	color.SetHWColor(_color);
	borderColorNorm.SetHWColor(0xFFAAAAAA);		// default white
	borderColorOver.SetHWColor(0xFFFFFF00);
	borderColorFocused.SetHWColor(0xFF00FFFF);
	borderColor = borderColorNorm;
	padTop      = 1.0f;
	padLeft     = 3.0f;
	cursorXOffset = 0.07f;
	cursorYOffset = 0.0f;
	blinkInterval = 0.62f;

	text = new char[capacity + 1];
	SetText(_text);
	pos = size;					// size will be initialized in SetText()

	updateCursorPos();
}

TextInput::~TextInput()
{
	delete [] text;
}

bool TextInput::KeyClick(int key, int chr)
{
	if (
		key >= HGEK_0 && key <= HGEK_9
		|| key >= HGEK_A && key <= HGEK_Z
		|| key == HGEK_GRAVE
		|| key == HGEK_MINUS
		|| key == HGEK_EQUALS
		|| key == HGEK_BACKSLASH
		|| key == HGEK_LBRACKET
		|| key == HGEK_RBRACKET
		|| key == HGEK_SEMICOLON
		|| key == HGEK_APOSTROPHE
		|| key == HGEK_COMMA
		|| key == HGEK_PERIOD
		|| key == HGEK_SLASH
		|| key == HGEK_SPACE
		) {
			insertChr(chr);
	}
	else if (key == HGEK_TAB && tabInsert) {
		// the font doesn't have tab char
		insertChr(' ');
		insertChr(' ');
		insertChr(' ');
		insertChr(' ');
	}
	else if (key == HGEK_ENTER) {
		if (isMultiLine)
			insertChr('\n');
		else if (enterHook)		// test for zero
			enterHook();
	}
	else if (key == HGEK_LEFT) 	backwardChar();
	else if (key == HGEK_RIGHT) forwardChar();
	else if (key == HGEK_BACKSPACE) backspace();
	return false;
}

bool TextInput::SetText(const char *_text)
{
	// be better
	for (size = 0; _text[size] != 0; ++size)
	{
		if (size > capacity && !expand())
			return false;
		if (_text[size] == '\n')
			isMultiLine = true;
		text[size] = _text[size];
	}
	text[size] = 0;
	pos = size;
	return true;
}

bool TextInput::insertChr(int chr)
{
	if (size == maxSize)
		return false;
	if (size == capacity) {
		if (!expand())
			return false;
	}
	for (int i = size; i > pos; --i)
		text[i] = text[i - 1];
	text[pos++] = chr;
	text[++size] = 0;
	return true;
}

bool TextInput::backspace()
{
	if (pos == 0) return false;
	strcpy(text + pos - 1, text + pos);
	--pos;
	text[--size] = 0;
	return true;
}

bool TextInput::backwardChar()
{
	if (pos == 0) return false;
	--pos;
	return true;
}

bool TextInput::forwardChar()
{
	if (pos == size) return false;
	++pos;
	return true;
}
bool TextInput::deleteChar()
{
	if (pos == size) return false;
	++pos;
	return backspace();
}

bool TextInput::expand()
{
	capacity *= 2;
	char *textNew;
	try {
		textNew = new char[capacity + 1];
	} catch (...) {
		capacity /= 2;
		return false;
	}
	strcpy(textNew, text);
	delete [] text;
	text = textNew;
	return true;
}

void TextInput::updateCursorPos()
{
	blinkCounter = 0;				// do not blink while moving
	cursorX = rect.x1 + padLeft + cursorXOffset;
	cursorY = rect.y1 + padTop + cursorYOffset;

	if (!isMultiLine) {
		cursorX += fnt->GetStringWidth(text)
			- fnt->GetStringWidth(text + pos);
	} else {
		int line = 0;
		int nlPos = -1;		// last newline position
		int i;
		for (i = 0; i < pos; ++i) {
			if (text[i] == '\n') {
				++line;
				nlPos = i;
			}
		}
		cursorX += fnt->GetStringWidth(text + nlPos + 1, false)
			- fnt->GetStringWidth(text + pos, false);
		cursorY += line * fnt->GetHeight();
	}
}

void TextInput::Render()
{
	isPositionUpdate = true;
	calcAbsolutePosition();
	hge->Gfx_RenderLine(rect.x1, rect.y1, rect.x2, rect.y1, borderColor.GetHWColor());
	hge->Gfx_RenderLine(rect.x2, rect.y1, rect.x2, rect.y2, borderColor.GetHWColor());
	hge->Gfx_RenderLine(rect.x2, rect.y2, rect.x1, rect.y2, borderColor.GetHWColor());
	hge->Gfx_RenderLine(rect.x1, rect.y2, rect.x1, rect.y1, borderColor.GetHWColor());
	// render a cursor beam
	if (focused && blinkCounter < blinkInterval)
		hge->Gfx_RenderLine(cursorX, cursorY, cursorX,
		cursorY + fnt->GetHeight(), color.GetHWColor());
	// render the text
	fnt->SetColor(color.GetHWColor());
	fnt->Render(rect.x1 + padLeft, rect.y1 + padTop, HGETEXT_LEFT, text);

}